package co.baiku.boot.controller;

import co.baiku.boot.common.message.Message;
import co.baiku.boot.common.validate.Validation;
import co.baiku.boot.core.orm.dto.AuthDetailDto;
import co.baiku.boot.core.orm.dto.UserDetailDto;
import co.baiku.boot.tools.JwtTools;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/")
public class WebController {
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private UserDetailsService userDetailsService;
    private JwtTools jwtTools = JwtTools.ME;

    @GetMapping("/login")
    public Message login(String username, String password) {
        //用户验证
        final Authentication authentication = authenticate(username, password);
        //存储认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //生成token
        final UserDetailDto userDetail = (UserDetailDto) authentication.getPrincipal();
        final String token = jwtTools.generateAccessToken(userDetail);
        //存储token
        jwtTools.putToken(userDetail.getId(), token);
        userDetail.setToken(token);
        return Message.result(userDetail);
    }

    @GetMapping("/reg")
    public UserDetailDto reg(String username, String password) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        System.out.println(encoder.encode(password));
        UserDetailDto userDetail = new UserDetailDto();
        userDetail.setUsername(username);
        userDetail.setPassword(encoder.encode(password));
        userDetail.setLastPasswordResetAt(LocalDateTime.now());
        userDetail.setId(2L);
        List<AuthDetailDto> roles = new ArrayList<>();
        AuthDetailDto roleDetailDto = new AuthDetailDto("Admin");
        roles.add(roleDetailDto);
        userDetail.setRoles(roles);
        return userDetail;
    }

    @GetMapping("/logout")
    public void logout(@RequestHeader("Authorization") String token) {
        Long userId = jwtTools.getUserIdFromToken(token);
        jwtTools.deleteToken(userId);
    }

    @PreAuthorize("hasAuthority('Admin')")
    @GetMapping("/refresh")
    public UserDetailDto refresh(@RequestHeader("Authorization") String oldToken) {
        String username = jwtTools.getUsernameFromToken(oldToken);
        UserDetailDto userDetail = (UserDetailDto) userDetailsService.loadUserByUsername(username);
        if (jwtTools.canTokenBeRefreshed(oldToken, userDetail.getLastPasswordResetAt())) {
            String token = jwtTools.refreshToken(oldToken);
            userDetail.setToken(token);
            return userDetail;
        }
        return null;
    }

    @PreAuthorize("hasAuthority('Admin')")
    @GetMapping("/getUserByToken")
    public UserDetailDto getUserByToken(@RequestHeader("Authorization") String token) {
        return jwtTools.getUserFromToken(token);
    }

    private Authentication authenticate(String username, String password) {
        try {
            //该方法会去调用userDetailsService.loadUserByUsername()去验证用户名和密码，如果正确，则存储该用户名密码到“security 的 context中”
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw Validation.error("用户不存在或已禁用");
        } catch (BadCredentialsException ex) {
            throw Validation.error("用户名或密码错误");
        }
    }
}
